package framework.world;

import framework.interfaces.Cell;
import framework.world.World;

/**
 * Implements a cell for the framework.world. Uses the Cell interface to be the library
 * frontend of this class
 */
public class CellImpl implements Cell{

	private static int NUM_STATES; //Max number of states each cell can be in
	protected static int DEFAULT_STATE; //Default state of each cell
	protected static int NEIGHBORHOOD_SIZE; //How big the neighborhood of a cell is
	protected static World WORLD; //The framework.world the cell inhabits
	protected int state; //What state the cell is currently in
	private int row; //What row in the framework.world the cell is in
	private int col; //What column in the framework.world the cell is in
	
	
	/**
	 * Creates a new CellImpl object
	 * @param row The row in the framework.world
	 * @param col The column in the framework.world
	 * @param startingState The state the cell starts in
	 */
	public CellImpl(int row, int col, int startingState){
		this.row = row;
		this.col = col;
		state = startingState;
	}
	
	/*
	 * @see framework.interfaces.Cell#getNeighborCount(int)
	 */
	public int getNeighborCount(int state){
		int stateCount = 0;
		int dirCount = 0;
		
		for(Direction dir: Direction.values()){
			//Only check the directions in the specified neighborhood
			if(dirCount >= NEIGHBORHOOD_SIZE){
				break;
			}
			//Count number of cells in state
			if(WORLD.getAdjacentCellState(this, dir) == state){
				stateCount++;
			}
			dirCount++;
		}
		return stateCount;
	}
	
	/*
	 * @see framework.interfaces.Cell#getState()
	 */
	public int getState(){
		return state;
	}
	
	/**
	 * The state the cell should be set to
	 * As long as newState is valid, the cell will be set to that state
	 * Otherwise, it will be set to the DEFAULT_STATE
	 * @param newState The new state
	 */
	public void setState(int newState){
		if(newState < getNumStates() && newState >= 0){
			state = newState;
		}
		else{
			state = DEFAULT_STATE;
		}
	}
	
	/**
	 * The max number of states any cell can have
	 * @param newNumStates New max state value. Must be >= 1
	 * @throws IllegalArgumentException If newNumStates is < 1
	 */
	protected static void setNumStates(int newNumStates) throws IllegalArgumentException{
		if(newNumStates >= 1){
			NUM_STATES = newNumStates;
		}
		else{
			throw new IllegalArgumentException("Can't have less than one state!");
		}
	}
	
	/**
	 * Sets the new default state
	 * @param newDefaultState The new default state value. Must be 0<=new<numStates
	 * @throws IllegalArgumentException If newDefault state is not within bounds
	 */
	protected static void setDefaultState(int newDefaultState) throws IllegalArgumentException{
		if(newDefaultState >= 0 && newDefaultState < getNumStates()){
			DEFAULT_STATE = newDefaultState;
		}
		else{
			throw new IllegalArgumentException("Illegal Default State value!");
		}
	}
	
	/**
	 * Get the row the cell is in
	 * @return The row the cell is in
	 */
	public int getRow(){
		return row;
	}
	
	/**
	 * Get the column the cell is in
	 * @return the column the cell is in
	 */
	public int getCol(){
		return col;
	}
	
	/**
	 * Gets the number of states any cell can have
	 * @return the number of states any cell can have
	 */
	protected static int getNumStates() {
		return NUM_STATES;
	}

	/*
	 * @see framework.interfaces.Cell#getAdjacentCellState(framework.world.Direction)
	 */
	public int getAdjacentCellState(Direction dir) {
		return WORLD.getAdjacentCellState(this, dir);
	}

	/*
	 * @see framework.interfaces.Cell#setMachineState(int)
	 */
	@Override
	public void setMachineState(int newMachineState) {
		WORLD.setMachineState(newMachineState);
	}

	/*
	 * @see framework.interfaces.Cell#getMachineState()
	 */
	@Override
	public int getMachineState() {
		return WORLD.getMachineState();
	}
}
